#ifndef __MATHTOOLS_AABBOX3DF_H_INCLUDE__
#define __MATHTOOLS_AABBOX3DF_H_INCLUDE__
#include "mathToolsConfig.h"
#include "vec3f.h"
#include "plane.h"


namespace mt
{
	class ray3df;
	class line3df;

	//! Axis aligned bounding box in 3d dimensional space.
	/** Has some useful methods used with occlusion culling or clipping.
	*/
	class aabbox3df
	{
	public:

		//! Default Constructor.
		aabbox3df();
		//! Constructor with min edge and max edge.
		aabbox3df(const vec3f& min, const vec3f& max);
		//! Constructor with only one point.
		aabbox3df(const vec3f& init);
		//! Constructor with min edge and max edge as single values, not vectors.
		aabbox3df(float minx, float miny, float minz, float maxx, float maxy, float maxz);
		// operators
		//! Equality operator
		/** \param other box to compare with.
		\return True if both boxes are equal, else false. */
		inline bool operator==(const aabbox3df& other) const;
		//! Inequality operator
		/** \param other box to compare with.
		\return True if both boxes are different, else false. */
		inline bool operator!=(const aabbox3df& other) const;

		// functions

		//! Resets the bounding box to a one-point box.
		/** \param x X coord of the point.
		\param y Y coord of the point.
		\param z Z coord of the point. */
		void reset(float x, float y, float z);

		//! Resets the bounding box.
		/** \param initValue New box to set this one to. */
		void reset(const aabbox3df& initValue);

		//! Resets the bounding box to a one-point box.
		/** \param initValue New point. */
		void reset(const vec3f& initValue);

		//! Adds a point to the bounding box
		/** The box grows bigger, if point was outside of the box.
		\param p: Point to add into the box. */
		void addInternalPoint(const vec3f& p);

		//! Adds another bounding box
		/** The box grows bigger, if the new box was outside of the box.
		\param b: Other bounding box to add into this box. */
		void addInternalBox(const aabbox3df& b);

		//! Adds a point to the bounding box
		/** The box grows bigger, if point is outside of the box.
		\param x X coordinate of the point to add to this box.
		\param y Y coordinate of the point to add to this box.
		\param z Z coordinate of the point to add to this box. */
		void addInternalPoint(float x, float y, float z);

		//! Get center of the bounding box
		/** \return Center of the bounding box. */
		vec3f getCenter() const;

		//! Get extent of the box (maximal distance of two points in the box)
		/** \return Extent of the bounding box. */
		vec3f getExtent() const;

		//! Check if the box is empty.
		/** This means that there is no space between the min and max edge.
		\return True if box is empty, else false. */
		bool isEmpty() const;

		//! Get the volume enclosed by the box in cubed units
		float getVolume() const;

		//! Get the surface area of the box in squared units
		float getArea() const;

		//! Stores all 8 edges of the box into an array
		/** \param edges: Pointer to array of 8 edges. */
		void getEdges(vec3f *edges) const;

		//! Repairs the box.
		/** Necessary if for example MinEdge and MaxEdge are swapped. */
		void repair();

		//! Calculates a new interpolated bounding box.
		/** d=0 returns other, d=1 returns this, all other values blend between
		the two boxes.
		\param other Other box to interpolate between
		\param d Value between 0.0f and 1.0f.
		\return Interpolated box. */
		aabbox3df getInterpolated(const aabbox3df& other, float d) const;

		//! Determines if a point is within this box.
		/** Border is included (IS part of the box)!
		\param p: Point to check.
		\return True if the point is within the box and false if not */
		bool isPointInside(const vec3f& p) const;

		//! Determines if a point is within this box and not its borders.
		/** Border is excluded (NOT part of the box)!
		\param p: Point to check.
		\return True if the point is within the box and false if not. */
		bool isPointTotalInside(const vec3f& p) const;

		//! Check if this box is completely inside the 'other' box.
		/** \param other: Other box to check against.
		\return True if this box is completly inside the other box,
		otherwise false. */
		bool isFullInside(const aabbox3df& other) const;

		//! Determines if the axis-aligned box intersects with another axis-aligned box.
		/** \param other: Other box to check a intersection with.
		\return True if there is an intersection with the other box,
		otherwise false. */
		bool intersectsWithBox(const aabbox3df& other) const;

		//! Tests if the box intersects with a line
		/** \param line: Line to test intersection with.
		\return True if there is an intersection , else false. */
		bool intersectsWithLine(const line3df& line) const;

		//! Tests if the box intersects with a ray
		bool intersectsWithRay(const ray3df& ray, const float len)const;

		//! Tests if the box intersects with a line
		/** \param linemiddle Center of the line.
		\param linevect Vector of the line.
		\param halflength Half length of the line.
		\return True if there is an intersection, else false. */
		bool intersectsWithLine(const vec3f& linemiddle,
			const vec3f& linevect, float halflength) const;

		//! Classifies a relation with a plane.
		/** \param plane Plane to classify relation to.
		\return Returns ISREL3D_FRONT if the box is in front of the plane,
		ISREL3D_BACK if the box is behind the plane, and
		ISREL3D_CLIPPED if it is on both sides of the plane. */
		EIntersectionRelation3D classifyPlaneRelation(const plane& plane) const;

		//! The near edge
		vec3f MinEdge;

		//! The far edge
		vec3f MaxEdge;
	};

}

#endif//! __MATHTOOLS_AABBOX3DF_H_INCLUDE__